; Copyright 2013 padnest@gmail.com

; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;   http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.


	LIST p=18F4431
	#include <p18f4431.inc>
    #include pic18f-langext-v1.3.inc

; configs
	CONFIG OSC = HS
	CONFIG IESO = OFF
	CONFIG FCMEN = OFF
	CONFIG PWRTEN = ON
	CONFIG WDTEN = OFF
	CONFIG BOREN = OFF
	CONFIG MCLRE = ON
	CONFIG LVP = OFF
	CONFIG DEBUG = OFF
	CONFIG STVREN = ON

; registers
	UDATA
delay1		res 1
delay2		res 1
delay3		res 1
temp		res 1
count		res 1

#DEFINE LCD_RS			PORTC, RC4
#DEFINE LCD_E			PORTC, RC5
#DEFINE LCD_DATA		PORTC
#DEFINE LCD_TRIS		TRISC

; reset vector
	ORG	0x0000
	goto	main

; interrupt vector in compatibility mode
	ORG	0x0008

interrupt:
	retfie	FAST            ; exit from interrupt


main:
	; init ports
    movlf   b'00000000', LCD_TRIS		; all output
	clrf	LCD_DATA

	; init LCD
	call	LCD_init

	clrf	TBLPTRU
	; write to LCD at line 1 pos 0
	mov16lf	str_helloWorld, TBLPTRH, TBLPTRL
	call	LCD_writeString
	; write to LCD at line 2 at pos 17
	movlw	0x11
	call	LCD_setLine2Pos
	mov16lf	str_padNest, TBLPTRH, TBLPTRL
	call	LCD_writeString

loop8:							; infinite loop
	call	LCD_scrollLeft
	call	sub_delay125ms
	goto	loop8


str_helloWorld:
    da    "Hello World!!!", 0
str_padNest:
    da    "padnest.blogspot.com", 0


LCD_writeString:
	TBLRD*+								; read data in TABLAT with post increment
	movf	TABLAT, W					; get data value
	bz		LCD_writeString_return		; is 0? (end string)
	call	LCD_writeChar				; no: write char
	bra		LCD_writeString				; and repeat
LCD_writeString_return:
	return								; yes: return

LCD_scrollLeft:
	movlw	b'00011000'					; shift all screen to left
	call	LCD_writeCmd
	return

LCD_scrollRight:
	movlw	b'00011100'					; shift all screen to right
	call	LCD_writeCmd
	return

LCD_init:
	call	sub_delay40ms				; wait a little bit from power up

	; send reset sequence
	movlf	b'00000011', LCD_DATA		
	call	LCD_strobeE
	call	sub_delay5ms
	call	LCD_strobeE
	call	sub_delay160us
	call	LCD_strobeE
	call	sub_delay160us
	movlf	b'00000010', LCD_DATA		; we want 4 bit bus len
	call	LCD_strobeE
	call	sub_delay160us

	; now we can send configuration commands in 4 bit bus mode
	movlw	b'00101000'					; Function set: 4 bit data, 2 lines, font 5x8
	call	LCD_writeCmd
	call	LCD_clr						; clear DDRAM
	movlw	b'00000110'					; Entry mode set: increment, screen scroll off
	call	LCD_writeCmd
	movlw	b'00001100'					; Display on/off control: display on, cursor off, blanking off
	call	LCD_writeCmd
	return


LCD_clr:
	movlw	b'00000001'					; Clear display
	call	LCD_writeCmd
	call	sub_delay5ms
	return

LCD_setLine1Pos:
	iorlw	0x80						; Set DDRAM address
	call	LCD_writeCmd
	call	sub_delay60us
	return

LCD_setLine2Pos:
	addlw	0x40						; line 2 starts at address 0x40
	iorlw	0x80						; Set DDRAM address
	call	LCD_writeCmd
	call	sub_delay60us
	return

LCD_writeChar:
	movwf	temp				; save the value to send
	swapf	temp, W				; Most Significant Nibble
	andlw	0x0F
	movwf	LCD_DATA
	bsf		LCD_RS				; RS=1 for DDRAM writing
	call	LCD_strobeE
	movf	temp, W				; Least Significant Nibble
	andlw	0x0F
	movwf	LCD_DATA
	bsf		LCD_RS				; RS=1 for DDRAM writing
	call	LCD_strobeE
	call	sub_delay60us		; wait for write execution
	return

LCD_writeCmd:
	movwf	temp				; save the value to send
	swapf	temp, W				; Most Significant Nibble
	andlw	0x0F
	movwf	LCD_DATA
	bcf		LCD_RS				; RS=0 for commands
	call	LCD_strobeE
	movf	temp, W				; Least Significant Nibble
	andlw	0x0F
	movwf   LCD_DATA
	bcf		LCD_RS				; RS=0 for commands
	call	LCD_strobeE
	call	sub_delay60us		; wait for command execution
	return

LCD_strobeE:
	bsf LCD_E					; E=1
	nop
	bcf LCD_E					; E=0
	return


sub_delay125ms:					; 250000 cycles @ FOSC 8 MHZ (including call)
	movlw	0x4E
	movwf	delay1
	movlw	0xC4
	movwf	delay2
sub_delay125ms_2:
	decfsz	delay1, f
	goto	sub_delay125ms_3
	decfsz	delay2, f
sub_delay125ms_3:
	goto	sub_delay125ms_2
	goto	sub_delay125ms_4
sub_delay125ms_4:
	nop
	return

sub_delay40ms:					; 80000 cycles @ FOSC 8 MHZ (including call)
	movlw	0x7E
	movwf	delay1
	movlw	0x3F
	movwf	delay2
sub_delay40ms_2:
	decfsz	delay1, f
	goto	sub_delay40ms_3
	decfsz	delay2, f
sub_delay40ms_3:
	goto	sub_delay40ms_2
	goto	sub_delay40ms_4
sub_delay40ms_4:
	nop
	return

sub_delay5ms:					; 10000 cycles @ FOSC 8 MHZ (including call)
	movlw	0xCE
	movwf	delay1
	movlw	0x08
	movwf	delay2
sub_delay5ms_2:
	decfsz	delay1, f
	goto	sub_delay5ms_3
	decfsz	delay2, f
sub_delay5ms_3:
	goto	sub_delay5ms_2
	goto	sub_delay5ms_4
sub_delay5ms_4:
	nop
	return

sub_delay160us:					; 320 cycles @ FOSC 8 MHZ (including call)
	movlw	0x7E
	movwf	delay1
	movlw	0x03
	movwf	delay2
sub_delay160us_2:
	decfsz	delay1, f
	goto	sub_delay160us_3
	decfsz	delay2, f
sub_delay160us_3:
	goto	sub_delay160us_2
	goto	sub_delay160us_4
sub_delay160us_4:
	nop
	return

sub_delay60us:					; 120 cycles @ FOSC 8 MHZ (including call)
	movlw	0x26
	movwf	delay1
sub_delay60us_2:
	decfsz	delay1, f
	goto	sub_delay60us_2
	nop
	return

	END